iOS 开发中一些实用小代码

本文介绍了 iOS 实际开发过程中的一些实用代码段。

代码段

添加 pch 文件

pre-Compile Header(预编译头文件),一般存放一些使用频率较高的代码或文件。尽量不要存放太多内容,如果预编译的代码太多的话,会增加程序的启动时间。

Command + N 打开新建文件窗口,选择 iOS –> Other –> PCH File 创建 pch 文件,如图:

点击 Next

点击 Create

PCH 文件创建好之后需要配置一下,如图:

将图片中的 Precompile Prefix Header 右边的NO改为Yes。预编译后的pch文件会被缓存起来,可以提高编译速度。

同时编辑 Prefix Header ,如图:

这是相对路径的写法,pch指的是项目名,pch.pch指的是项目中pch文件的文件名和后缀。

到这里 PCH 文件已经创建并配置完成,最后一步是把需要预编译的代码放到 pch.pch 中,如图:

这里我设置了两个宏定义(屏幕宽和屏幕高)并引入了一个Common.h的头文件。

然后你就发现 kSCREEN_WIDTH 和 kSCREEN_HEIGHT 变成全局的了。

正则表达式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
// 验证邮箱
+ (BOOL)validateEmail:(NSString *)email
{
NSString *pattern = @"[A-Z0-9a-z._%+-]+@[A-Za-z0-9.-]+\\.[A-Za-z]{2,4}";
NSPredicate *pred = [NSPredicate predicateWithFormat:@"SELF MATCHES %@", pattern];
BOOL isMatch = [pred evaluateWithObject:email];
return isMatch;
}

// 验证手机号
+ (BOOL)validateMobile:(NSString *)mobile
{
// 以1开头,第二位是3,5,7,8的11位整数
NSString *pattern = @"^1[3578]\\d{9}$";
NSPredicate *pred = [NSPredicate predicateWithFormat:@"SELF MATCHES %@", pattern];
BOOL isMatch = [pred evaluateWithObject:mobile];
return isMatch;
}

// 验证用户名
+ (BOOL)validateNickName:(NSString *)userName
{
//2-10个中英文、数字和下划线
//NSString *pattern = @"[\u4e00-\u9fa5_a-zA-Z0-9]{2,10}";
// \S匹配任意可见字符
NSString *pattern = @"\\S{0,10}";
NSPredicate *pred = [NSPredicate predicateWithFormat:@"SELF MATCHES %@", pattern];
BOOL isMatch = [pred evaluateWithObject:userName];
return isMatch;
}

// 验证密码
+ (BOOL)validatePassword:(NSString *)password
{
//6-20位的数字字母组合-- ^表示已...开头,$表示已...结尾
NSString *pattern = @"^[a-zA-Z0-9]{6,18}+$";
NSPredicate *pred = [NSPredicate predicateWithFormat:@"SELF MATCHES %@", pattern];
BOOL isMatch = [pred evaluateWithObject:password];
return isMatch;
}

参考网站:IOS常用正则表达式

点击状态栏不返回顶部问题

1
@property(nonatomic) BOOL  scrollsToTop __TVOS_PROHIBITED;          // default is YES.

官方文档说:当一个控制器只有一个 scrollView 或 scrollView 的子类(tableView,collectionView等),并且把这个属性设置为 YES,点击状态栏才能回到顶部;当有2个或以上的scrollView,系统不知道你需要操作的是那个scrollView,因此需要将其他的scrollView的这个属性设置为 NO。

导航栏和状态栏

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// 隐藏导航栏
[self.navigationController setNavigationBarHidden:YES animated:YES];

// 隐藏状态栏
[[UIApplication sharedApplication] setStatusBarHidden:YES];

// 设置导航栏是否半透明
self.navigationController.navigationBar.translucent = YES;

// 导航栏变为透明
[self.navigationController.navigationBar setBackgroundImage:[UIImage new] forBarMetrics:0];
// 让黑线消失的方法
self.navigationController.navigationBar.shadowImage=[UIImage new];

// 状态栏的网络活动风火轮是否旋转
[UIApplication sharedApplication].networkActivityIndicatorVisible = YES ; //默认值是NO。

for循环和while循环

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21

for (NSString *str in array) {

}

// 先执行i = 0(只执行一次);再执行循环体,最后执行i++;
for (int i=0; i<10; i++) {
// 循环体
}

// 等同于for循环
// 当循环到结尾然后需要从头再次循环时使用,for循环不可以。
int i = 0;
while (i < 10) {
if(i == 9)
{
i = 0;
}
NSLog(@"i: %d", i);
i ++;
}

截取屏幕图片

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
//创建一个基于位图的图形上下文并指定大小为CGSizeMake(200,400) 
UIGraphicsBeginImageContext(CGSizeMake(200,400));

//renderInContext 呈现接受者及其子范围到指定的上下文
//self.view.layer 表示要截屏的视图
[self.view.layer renderInContext:UIGraphicsGetCurrentContext()];

//返回一个基于当前图形上下文的图片
UIImage *aImage = UIGraphicsGetImageFromCurrentImageContext();

//移除栈顶的基于当前位图的图形上下文
UIGraphicsEndImageContext();

//以png格式返回指定图片的数据
imageData = UIImagePNGRepresentation(aImage);

图片压缩

方案一:文件体积变小,但是像素没变,长宽尺寸不变,可能导致图片质量下降,并且有一个压缩最小值。

1
NSData *data=UIImageJPEGRepresentation(image, 0.0f);

方案二:裁剪图片,图片像素数减少,长宽尺寸减小,图片体积也减少

1
2
3
4
5
6
7
8
9
10
- (NSData *)imageWithImage:(UIImage*)image
scaledToSize:(CGSize)newSize;
{
UIGraphicsBeginImageContext(newSize);
[image drawInRect:CGRectMake(0,0,newSize.width,newSize.height)];
UIImage* newImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
// 有时需要两种方案配合使用
return UIImageJPEGRepresentation(newImage, 0.8);
}

上传图片

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
AFHTTPRequestOperationManager *manager = [AFHTTPRequestOperationManager manager];
[manager POST:url parameters:parameters constructingBodyWithBlock:^(id<AFMultipartFormData> formData) {

NSData *imgData = UIImageJPEGRepresentation(image, 0.5);
/**
* name:部分是服务器用来解析的字段
* fileName:则是直接上传上去的图片,注意一定要加 .jpg或者.png,(这个根据你得到这个imgData是通过jepg还是png的方式来获取决定)。
* mimeType:值也要与上面的类型对应,网上看到有的说直接写成 @"image/*", 据说也是可以的, 没验证过。
*/
[formData appendPartWithFileData:imgData name:@"avatar" fileName:@"avatar.jpg" mimeType:@"image/jpeg"];

} success:^(AFHTTPRequestOperation *operation, id responseObject) {

} failure:^(AFHTTPRequestOperation *operation, NSError *error) {

}];

UITableViewWrapperView 和UITableView 的 frame 不同

1
2
3
4
5
6
7
8
解决方案
- (void)viewWillLayoutSubviews
{
[super viewWillLayoutSubviews];

self.tableView.contentInset = UIEdgeInsetsZero;
self.tableView.scrollIndicatorInsets = UIEdgeInsetsZero;
}

iOS 自定义TextView/TextField光标颜色、长度或高度

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
1. TextView/TextField光标颜色可通过设置tintColor属性进行修改:

self.textView.tintColor = [UIColor redColor];

2. TextView/TextField自定义光标长度或高度, 可通过重写父类方法caretRectForPosition:实现, 具体设置如下:

@interface CustomTextView : UITextView

重写父类方法:

- (CGRect)caretRectForPosition:(UITextPosition *)position
{
CGRect originalRect = [super caretRectForPosition:position];
originalRect.origin.x = 8;
originalRect.origin.y = 10;
originalRect.size.height = self.font.lineHeight;
originalRect.size.width = 1;

return originalRect;
}

输入框明文和密文切换问题

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
1. 明文和密文切换末尾空白

// 在转换方法中添加以下代码
- (void)switchAction:(id)sender {
self.passwordTextField.secureTextEntry = !self.passwordTextField.secureTextEntry;
NSString* text = self.passwordTextField.text;
self.passwordTextField.text = @" ";
self.passwordTextField.text = text;
}

2. 明文和密文切换再次输入会清空输入内容

// 在代理方法中添加以下代码

-(BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string{

NSString * toBeString = [textField.text stringByReplacingCharactersInRange:range withString:string];
if (textField == _passwordTF && textField.isSecureTextEntry ) {
textField.text = toBeString;
return NO;
}
return YES;
}

shareSDK问题

1
2
3
4
5
6
7
8
9
10

// 新浪微博分享--解决does multipart has image?问题, "说明传的图片的地址新浪微博解析不出来,会失败,可能是你的图片的url(测试地址,内网),新浪访问不了,再就是将ip换成域名即可。"
// 我解决的办法是:将设置参数方法的第三个参数image,由传链接的形式,改为直接用UIImage类型的对象

NSData * image_data = [[NSData alloc]initWithContentsOfURL:[NSURL URLWithString:_imageUrl]];
UIImage *sina_image = [UIImage imageWithData:image_data];
id<ISSCAttachment> _image = [ShareSDK pngImageWithImage:sina_image];

// 第三方登录--QQ
// 当客户端未安装时,不会调用网页版QQ。关于QZone网页授权:1,以前申请的应用可以网页授权,但现在新申请的网页应用不可以网页授权。2,ShareSDK之前因为用户反馈,有网页授权会通不过审核,所以不得已去掉了网页授权。

textView与textField限制输入字数问题

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34

// 一般采用这个方法
- (BOOL)textView:(UITextView *)textView shouldChangeTextInRange:(NSRange)range replacementText:(NSString *)text
{
//判断输入的字符,是否超过界限
NSString *str = [NSString stringWithFormat:@"%@%@", textView.text, text];
if (str.length > kMaxLen)
{
textView.text = [str substringToIndex:kMaxLen];
return NO;
}
return YES;
}

// 但是,在汉语拼音输入状态下,如果有键盘联想汉字输入,则无法回调到该函数,所以可以一直联想输入下去,做不到限制字符数量的目的,为了解决这个问题,我们在textViewDidChange进行限制。

- (void)textViewDidChange:(UITextView *)textView
{
if ( textView.text.length > kMaxLen)
{
textView.text = [textView.text substringToIndex:kMaxLen];
}
}

// 此时,如果在拼音状态下输入会有crash ,因为汉语拼音状态下,联想出的汉字等待用户选择,在用户选择前的状态下,不能改变textView.text。解决办法:

- (void)textViewDidChange:(UITextView *)textView
{
if (textView.markedTextRange == nil && textView.text.length > kMaxLen)
//加上 textView.markedTextRange == nil判断,当此属性为nil时,代表不在这种联想输入等待确定状态。
{
textView.text = [textView.text substringToIndex:kMaxLen];
}
}

修改tableView的分割线

1
2
3
4
5
 if ([tableView respondsToSelector:@selector (setSeparatorInset:)]) {

[tableView setSeparatorInset:UIEdgeInsetsZero];
[tableView setSeparatorColor:kGetColor ( 18.0 , 18.0 , 18.0 )];
}

UITableView 的plain样式分组名悬浮问题

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#pragma mark - 禁止session滚动
- (void)scrollViewDidScroll:(UIScrollView *)scrollView {
-
if (scrollView == _tableView)
{
CGFloat sectionHeaderHeight = 25; //sectionHeaderHeight

if (scrollView.contentOffset.y<=sectionHeaderHeight&&scrollView.contentOffset.y>=0) {

scrollView.contentInset = UIEdgeInsetsMake(-scrollView.contentOffset.y, 0, 0, 0);

} else if (scrollView.contentOffset.y>=sectionHeaderHeight) {

scrollView.contentInset = UIEdgeInsetsMake(-sectionHeaderHeight, 0, 0, 0);

}else
{
scrollView.contentInset = UIEdgeInsetsMake(64, 0, 0, 0);
}
}
}

iOS 支持 ipv6 问题

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
1.AFNetworking 换成最新版本

2.友盟等与网络有关的第三方SDK换成最新版本

3.服务器请求地址不要使用ip,要使用域名

4.如果用到底层API需要单独处理,尽量使用NSURLSession

5.检测网络状况的类在ios8一下可能会有点问题

```

### 判断字符串是否为空

```bash
+ (BOOL)isEmpty:(NSString *)str
{
if (!str) {
return YES;
}else
{
NSCharacterSet *set=[NSCharacterSet whitespaceAndNewlineCharacterSet];

NSString *trimedString=[str stringByTrimmingCharactersInSet:set];

if (trimedString.length==0) {
return YES;
}else
{
return NO;
}
}
}

URLDecode 解码

1
2
3
4
5
6
+ (NSString *)URLDecodedString:(NSString *)str
{
NSString *decodedString=(__bridge_transfer NSString *)CFURLCreateStringByReplacingPercentEscapesUsingEncoding(NULL, (__bridge CFStringRef)str, CFSTR(""), CFStringConvertNSStringEncodingToEncoding(NSUTF8StringEncoding));

return decodedString;
}

textView 根据输入内容改变高度(最多显示5行)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
- (void)textViewDidChange:(UITextView *)textView
{
NSString *textViewText = [textView.text stringByReplacingOccurrencesOfString:@" " withString:@""];

//判断输入的字符是否是空格
if (![self isEmpty:textViewText]) {

CGFloat h = [inputTextView.layoutManager usedRectForTextContainer:inputTextView.textContainer].size.height;
inputTextView.contentSize = CGSizeMake(inputTextView.contentSize.width, h+20);
CGFloat five_h = hight_text_one*5.0f;
h = h>five_h?five_h:h;
CGRect frame = inputTextView.frame;
CGFloat diff = toolBar.frame.size.height - inputTextView.frame.size.height;
if (frame.size.height == h+20) {
if (h == five_h) {
[inputTextView setContentOffset:CGPointMake(0, inputTextView.contentSize.height - h - 20) animated:NO];
}
return;
}
frame.size.height = h+20;
inputTextView.frame = frame;
[inputTextView setContentOffset:CGPointZero animated:YES];

}else
{
// 处理输入为空格的情况
}
}

“Request failed: unacceptable content-type: text/plain(text/html)” 解决方案

1
2
3
4
5
6
7
此时需要修改AFNetworking可接收的Content-Type,前往AFNetworking源代码目录找到AFURLResponseSerialization.m文件将里面的代码:
self.acceptableContentTypes =[NSSetsetWithObjects:@"application/json",@"text/json",@"text/javascript",nil];
修改为:

self.acceptableContentTypes =[NSSet setWithObjects:@"application/json",@"text/json",@"text/javascript",@"text/plain" , nil];

self.acceptableContentTypes =[NSSet setWithObjects:@"application/json",@"text/json",@"text/javascript",@"text/html",nil];

UILabel的行间距,字间距以及高度计算

1
2


如果觉得写的不错,那就打赏一下吧!